home *** CD-ROM | disk | FTP | other *** search
/ Chip 2007 January, February, March & April / Chip-Cover-CD-2007-02.iso / Pakiet bezpieczenstwa / mini Pentoo LiveCD 2006.1 / mpentoo-2006.1.iso / livecd.squashfs / usr / lib / metasploit / src / shellcode / import.pl next >
Perl Script  |  2006-06-30  |  12KB  |  623 lines

  1. #!/usr/bin/perl
  2. ###############
  3. ##
  4. #
  5. # Shellcode auto-importing tool for MSF.  Takes raw payloads
  6. # and converts them to perl modules.
  7. #
  8. ##
  9. ###############
  10.  
  11. use strict;
  12. use FindBin qw($RealBin);
  13. use POSIX;
  14.  
  15. my $payloadDirectory = $RealBin . "/../../payloads/";
  16. my @payloads  = ();
  17. my $platforms = 
  18.     {
  19.         'i686'            => 'ia32',
  20.         'i586'            => 'ia32',
  21.         'i486'            => 'ia32',
  22.         'i386'            => 'ia32',
  23.         'sun4m'           => 'sparc',
  24.         'sun4u'           => 'sparc',
  25.         'sparc'           => 'sparc',
  26.         'Power Macintosh' => 'ppc',
  27.         'alpha'           => 'alpha',
  28.     };
  29. my @uinfo = POSIX::uname();
  30. my $mach  = $uinfo[4];
  31. my $arch  = $platforms->{$mach};
  32.  
  33. print "Loading payloads...\n";
  34.  
  35. LoadPayloads();
  36.  
  37. print "Loaded " . scalar(@payloads) . " payloads.\n";
  38.  
  39. TranslatePayloads();
  40.  
  41. exit;
  42.  
  43. #
  44. # Subs
  45. #
  46.  
  47. #
  48. # Loads all compatible payloads for translation
  49. #
  50. sub LoadPayloads
  51. {
  52.     RecurseDirectoryForPayloads(directory => $RealBin);
  53. }
  54.  
  55. #
  56. # Recurses the shellcode directory looking for payloads to import on the
  57. # platform being executed on
  58. #
  59. sub RecurseDirectoryForPayloads
  60. {
  61.     my ($directory) = @{{@_}}{qw/directory/};
  62.     my @sub;
  63.  
  64.     return undef if (!opendir(DIR, $directory));
  65.  
  66.     @sub = readdir(DIR);
  67.  
  68.     foreach my $child (@sub)
  69.     {
  70.         my $path = "$directory/$child";
  71.  
  72.         # If the path is a directory and it does not start with a slash, recurse
  73.         if ((-d $path) and
  74.             (!($child =~ /^\./)))
  75.         {
  76.             RecurseDirectoryForPayloads(directory => $path);
  77.         }
  78.         # If the path is a file and it ends in '.asm', add it to the list of
  79.         # payloads
  80.         elsif ((-f $path) and
  81.                ($path =~ /(.*)\.asm$/))
  82.         {
  83.             my $path = $1;
  84.  
  85.             # Skip architectures we don't support
  86.             next if (!($path =~ /$arch/));
  87.  
  88.             push @payloads, 
  89.                 {
  90.                     source   => $path . ".asm",
  91.                     hex      => $path . ".hex",
  92.                     raw      => $path . ".o",
  93.                     disasm   => $path . ".disasm",
  94.                     template => $path . ".template",
  95.                 };
  96.         }
  97.     }
  98.  
  99.     closedir(DIR);
  100. }
  101.  
  102. #
  103. # Translate payloads from source/raw information into perl modules
  104. #
  105. sub TranslatePayloads
  106. {
  107.     # Enumerate through all of the payloads
  108.     foreach my $payload (@payloads)
  109.     {
  110.         my $meta;
  111.         my $name;
  112.     
  113.         # Get the payloads meta information for use with translation
  114.         $meta = ExtractMetaInformation(
  115.             payload => $payload);
  116.  
  117.         next if (not defined($meta));
  118.  
  119.         if ((defined($meta->{'category'})) and
  120.             ($meta->{'category'} eq 'stager'))
  121.         {
  122.             print "Skipping stager import until better support is added.\n";
  123.             next;
  124.         }
  125.  
  126.         if ((defined($meta->{'importable'})) and
  127.             ($meta->{'importable'} eq 'no'))
  128.         {
  129.             print "Skipping non-importable payload: " . $payload->{'source'} . "\n";
  130.             next;
  131.         }
  132.         
  133.         # Get the payload's translated file name based on the meta information
  134.         $name = GetPayloadName(
  135.             meta => $meta);
  136.  
  137.         if (not defined($name))
  138.         {
  139.             print STDERR "Failed to determine translated filename for payload: " . $payload->{'source'} . "\n";
  140.             next;
  141.         }
  142.  
  143.         next if (!TranslatePayload(
  144.             name    => $name,
  145.             meta    => $meta,
  146.             payload => $payload));
  147.         
  148.         print "Translated $name\n";
  149.     }
  150. }
  151.  
  152. #
  153. # Extract information about how the payload should be imported from the source
  154. # file
  155. #
  156. sub ExtractMetaInformation
  157. {
  158.     my ($payload) = @{{@_}}{qw/payload/};
  159.     my $info;
  160.  
  161.     # Open the payload's source file
  162.     if (!open(SOURCE, $payload->{'source'}))
  163.     {
  164.         print STDERR "Failed to open payload: " . $payload->{'source'} . "\n";
  165.         return undef;
  166.     }
  167.  
  168.     # Enumerate the lines of the source file
  169.     while (<SOURCE>)
  170.     {
  171.         my $value;
  172.         my $tag;
  173.  
  174.         chomp($_);
  175.  
  176.         next if (!($_ =~ /META-(.+)=(.*)$/i));
  177.  
  178.         $tag   = $1;
  179.         $value = $2;
  180.  
  181.         $info->{lc($tag)} = $value;
  182.     }
  183.  
  184.     # Close source, baby!
  185.     close(SOURCE);
  186.  
  187.     return $info;
  188. }
  189.  
  190. #
  191. # Gets a payload's translated file name from its meta information
  192. #
  193. sub GetPayloadName
  194. {
  195.     my ($meta)   = @{{@_}}{qw/meta/};
  196.     my $filename = undef;
  197.     my $category = $meta->{'category'};
  198.     my $arch     = $meta->{'arch'};
  199.     my $name     = $meta->{'name'};
  200.     my $os       = $meta->{'os'};
  201.  
  202.     # Check to see if we have enough information to build the translated 
  203.     # filename
  204.     if ((not defined($arch)) or
  205.         (not defined($os)) or
  206.         (not defined($category)) or
  207.         (not defined($name)))
  208.     {
  209.         return undef;
  210.     }
  211.  
  212.     return $os . "_" . $arch . "_" . $category . "_" . $name;
  213. }
  214.  
  215. #
  216. # Translates an individual payload into a perl module
  217. #
  218. sub TranslatePayload
  219. {
  220.     my ($name, $meta, $payload) = @{{@_}}{qw/name meta payload/};
  221.     my $filename;
  222.     my $authorList = "";
  223.     my $contents = "";
  224.     my @authors;
  225.  
  226.     if (defined($meta->{'path'}))
  227.     {
  228.         $filename = $meta->{'path'};
  229.     } 
  230.     else
  231.     {
  232.         $filename = $payloadDirectory . $name . ".pm";
  233.     }
  234.  
  235.     # Translate the authors into a list for use in the perl module
  236.     if (defined($meta->{'authors'}))
  237.     {
  238.         @authors = split /,/, $meta->{'authors'};
  239.  
  240.         foreach (@authors)
  241.         {
  242.             $authorList .= "'$_', ";
  243.         }
  244.     }
  245.  
  246.     # Open the module file
  247.     if (!open(MODULE, ">$filename"))
  248.     {
  249.         print STDERR "Failed to open module file for writing: $filename\n";
  250.         return 0;
  251.     }
  252.  
  253.     # If a template file exists, use it
  254.     if (-f $payload->{'template'})
  255.     {
  256.         $contents = BuildPayloadFromCustomTemplate(
  257.             name    => $name,
  258.             meta    => $meta,
  259.             payload => $payload,
  260.             authors => $authorList);
  261.     }
  262.     else
  263.     {
  264.         $contents = BuildPayloadFromStandardTemplate(
  265.             name    => $name,
  266.             meta    => $meta,
  267.             payload => $payload,
  268.             authors => $authorList);
  269.     }
  270.  
  271.     # Write the module to the file and close, we win
  272.     print MODULE $contents;
  273.  
  274.     close(MODULE);
  275.  
  276.     return 1;
  277. }
  278.  
  279. #
  280. # Build the payload from a template file rather than from the standard template
  281. #
  282. sub BuildPayloadFromCustomTemplate
  283. {
  284.     my ($name, $meta, $payload, $authors) = @{{@_}}{qw/name meta payload authors/};
  285.     my $contents = "";
  286.     my $disasm = GetPayloadDisassembly(
  287.         payload => $payload);
  288.     my $hex = GetPayloadHexString(
  289.         payload => $payload);
  290.     my $shortname = $meta->{'shortname'};
  291.     my $description = $meta->{'description'};
  292.     my $arch = $meta->{'arch'};
  293.     my $os = $meta->{'os'};
  294.  
  295.     if (!open(TEMPLATE, $payload->{'template'}))
  296.     {
  297.         printf STDERR "Failed to open template file: " . $payload->{'template'} . "\n";
  298.         return undef;
  299.     }
  300.  
  301.     while (<TEMPLATE>)
  302.     {
  303.         $contents .= $_;
  304.     }
  305.  
  306.     close(TEMPLATE);
  307.  
  308.     # Replace template variables
  309.     $contents =~ s/__NAME__/$name/gm;
  310.     $contents =~ s/__SHORTNAME__/$shortname/gm;
  311.     $contents =~ s/__DESCRIPTION__/$description/gm;
  312.     $contents =~ s/__ARCH__/$arch/gm;
  313.     $contents =~ s/__OS__/$os/gm;
  314.     $contents =~ s/__AUTHORS__/$authors/gm;
  315.     $contents =~ s/__HEX__/$hex/gm;
  316.     $contents =~ s/__DISASM__/$disasm/gm;
  317.  
  318.     # Enumerate through all the custom defined variables
  319.     my $index = 1;
  320.     my $val;
  321.  
  322.     while (defined($val = $meta->{"custom$index"}))
  323.     {
  324.         my $var = "__CUSTOM" . $index . "__";
  325.  
  326.         $contents =~ s/$var/$val/gm;
  327.  
  328.         $index++;
  329.     }
  330.  
  331.     return $contents;
  332. }
  333.  
  334. #
  335. # Build the payload from the standard template
  336. #
  337. sub BuildPayloadFromStandardTemplate
  338. {
  339.     my ($name, $meta, $payload, $authors) = @{{@_}}{qw/name meta payload authors/};
  340.     my $contents = '';
  341.  
  342.     # Build out the module's contents
  343.     $contents .=
  344. "
  345. ##
  346. # This file is part of the Metasploit Framework and may be redistributed
  347. # according to the licenses defined in the Authors field below. In the
  348. # case of an unknown or missing license, this file defaults to the same
  349. # license as the core Framework (dual GPLv2 and Artistic). The latest
  350. # version of the Framework can always be obtained from metasploit.com.
  351. ##
  352.  
  353. package Msf::Payload::$name;
  354. use strict;
  355. ";
  356.  
  357.     # Does this payload have a base module from which it should inherit
  358.     if (defined($meta->{'basemod'}))
  359.     {
  360.         $contents .=
  361. "use base '" . $meta->{'basemod'} . "';\n";
  362.     }
  363.  
  364.     $contents .=
  365. "
  366. my \$info =
  367. {
  368.     'Name'        => '" . $meta->{'shortname'} . "',
  369.     'Version'     => '\$" . "Revision: " . "1.0 \$',
  370.     'Description' => '" . $meta->{'description'} . "',
  371.     'Authors'     => [ $authors ],
  372.     'Priv'        => 0,
  373.     'Size'        => 0,
  374.     'Arch'        => [ '" . $meta->{'arch'} . "' ],
  375.     'OS'          => [ '" . $meta->{'os'} . "' ],
  376. };
  377.  
  378. sub new
  379. {
  380.     my \$class = shift;
  381.     my \$hash  = \@_ ? shift : { };
  382.  
  383.     \$hash = \$class->MergeHashRec(\$hash, { 'Info' => \$info });
  384.  
  385.     my \$self = \$class->SUPER::new(\$hash, \@_);
  386.  
  387.     \$self->_Info->{'Size'} = \$self->_GenSize;
  388.  
  389.     return \$self;
  390. }
  391. ";
  392.  
  393.     # Get the Build, Generate, and GenSize functions that are specific to a 
  394.     # given category
  395.     $contents .= GetCategorySpecificModuleContents(
  396.         meta    => $meta,
  397.         payload => $payload);
  398.     
  399.     $contents .= 
  400. "
  401. 1;
  402. ";
  403.  
  404.     return $contents;
  405. }
  406.  
  407. #
  408. # Get contents that are specific to a given type of payload
  409. #
  410. sub GetCategorySpecificModuleContents
  411. {
  412.     my ($meta, $payload) = @{{@_}}{qw/meta payload/};
  413.     my $connectionType = $meta->{'connection-type'};
  414.     my $category = $meta->{'category'};
  415.     my $contents = "";
  416.     my $hex = GetPayloadHexString(
  417.         payload => $payload);
  418.  
  419.     # If the payload is connection based...
  420.     if (defined($connectionType))
  421.     {
  422.         my $offsets = 
  423.             {
  424.                 LHOST   => $meta->{'offset-lhost'},
  425.                 LPORT   => $meta->{'offset-lport'},
  426.                 RHOST   => $meta->{'offset-rhost'},
  427.                 RPORT   => $meta->{'offset-rport'},
  428.                 CPORT   => $meta->{'offset-cport'},
  429.                 FINDTAG => $meta->{'offset-findtag'},
  430.             };
  431.  
  432.         if ($connectionType eq 'reverse')
  433.         {
  434.             $contents = 
  435. "
  436. sub Build
  437. {
  438.     my \$self = shift;
  439.     return \$self->Generate(
  440.         \$self->GetVar('LHOST'), 
  441.         \$self->GetVar('LPORT'));
  442. }
  443.  
  444. sub Generate
  445. {
  446.     my \$self = shift;
  447.     my \$host = shift;
  448.     my \$port = shift;
  449.     my \$port_bin = pack('n', \$port);
  450.     my \$host_bin = gethostbyname(\$host);
  451.  
  452.     my \$shellcode = 
  453. $hex;    
  454.  
  455.     substr(\$shellcode, " . $offsets->{'LPORT'} . ", 2, \$port_bin);
  456.     substr(\$shellcode, " . $offsets->{'LHOST'} . ", 4, \$host_bin);
  457.  
  458.     return \$shellcode
  459. }
  460.  
  461. sub _GenSize
  462. {
  463.     my \$self = shift;
  464.     my \$bin  = \$self->Generate('127.0.0.1', '4444');
  465.     return length(\$bin);
  466. }
  467. ";
  468.         }
  469.         elsif ($connectionType eq 'bind')
  470.         {
  471.             $contents = 
  472. "
  473. sub Build
  474. {
  475.     my \$self = shift;
  476.     return \$self->Generate(
  477.         \$self->GetVar('LPORT'));
  478. }
  479.  
  480. sub Generate
  481. {
  482.     my \$self = shift;
  483.     my \$port = shift;
  484.     my \$port_bin = pack('n', \$port);
  485.  
  486.     my \$shellcode =
  487. $hex;
  488.  
  489.     substr(\$shellcode, " . $offsets->{'LPORT'} . ", 2, \$port_bin);
  490.  
  491.     return \$shellcode;
  492. }
  493.  
  494. sub _GenSize
  495. {
  496.     my \$self = shift;
  497.     my \$bin = \$self->Generate('4444');
  498.     return length(\$bin);
  499. }
  500. ";
  501.         }
  502.         elsif ($connectionType eq 'findtag')
  503.         {
  504.             $contents = 
  505. "
  506. sub Build
  507. {
  508.     my \$self = shift;
  509.     return \$self->Generate();
  510. }
  511.  
  512. sub Generate
  513. {
  514.     my \$self = shift;
  515.     my \$tag  = substr(\$self->GetLocal('FindTag') . (\"\\x01\" x 0x4), 0, 4);
  516.  
  517.     my \$shellcode =
  518. $hex;
  519.  
  520.     substr(\$shellcode, " . $offsets->{'FINDTAG'} . ", 4, \$tag);
  521.  
  522.     return \$shellcode;
  523. }
  524.  
  525. sub _GenSize
  526. {
  527.     my \$self = shift;
  528.     my \$bin  = \$self->Generate();
  529.     return length(\$bin);
  530. }
  531. ";
  532.         }
  533.     }
  534.     else
  535.     {
  536.         $contents =
  537. "
  538. sub Build
  539. {
  540.     my \$self = shift;
  541.     return \$self->Generate();
  542. }
  543.  
  544. sub Generate
  545. {
  546.     my \$self = shift;
  547.  
  548.     my \$shellcode =
  549. $hex;
  550.  
  551.     return \$shellcode;
  552. }
  553.  
  554. sub _GenSize
  555. {
  556.     my \$self = shift;
  557.     my \$bin  = \$self->Generate();
  558.     return length(\$bin);
  559. }
  560. ";
  561.     }
  562.  
  563.     # Include the payload disassembly
  564.     $contents .= GetPayloadDisassembly(
  565.         payload => $payload);
  566.  
  567.     return $contents;
  568. }
  569.  
  570. #
  571. # Get the payload's hex string that is formatted for inclusion in a perl module
  572. #
  573. sub GetPayloadHexString
  574. {
  575.     my ($payload) = @{{@_}}{qw/payload/};
  576.     my $hex = '';
  577.  
  578.     if (!open(HEX, $payload->{'hex'}))
  579.     {
  580.         print STDERR "Failed to open payload hex: " . $payload->{'hex'} . "\n";
  581.         return undef;
  582.     }
  583.  
  584.     while (<HEX>)
  585.     {
  586.         chomp($_);
  587.  
  588.         $hex .= " .\n" if (length($hex));
  589.         $hex .= "\t\t\"$_\"";
  590.     }
  591.  
  592.     close HEX;
  593.  
  594.     return $hex;
  595. }
  596.  
  597. #
  598. # Get the disassembly output for the payload and include it in the translated
  599. # version
  600. #
  601. sub GetPayloadDisassembly
  602. {
  603.     my ($payload) = @{{@_}}{qw/payload/};
  604.     my $disasm;
  605.  
  606.     if (!open(DISASM, $payload->{'disasm'}))
  607.     {
  608.         print STDERR "Failed to open payload disasm: " . $payload->{'disasm'} . "\n";
  609.         return undef;
  610.     }
  611.  
  612.     $disasm = "\n# Disassembly:\n#\n";
  613.  
  614.     while (<DISASM>)
  615.     {
  616.         chomp($_);
  617.  
  618.         $disasm .= "# $_\n";
  619.     }
  620.  
  621.     return $disasm;
  622. }
  623.